/** \file gu_scene_graph.h */

#pragma once

#include "gu_renderer.h"

#include "gr_containers.h"
#include "gr_numeric_types.h"
#include "gr_math.h"

#include<boost/lexical_cast.hpp>
#include<memory>

namespace gu
{
namespace scene_graph{

/** Property base type */
enum PropertyType{UNIFORMS, MATERIAL, RENDER_SETTINGS};

class Property
{
    public:
    typedef std::unique_ptr<Property> ptr;
    virtual ~Property() = 0;
    virtual PropertyType type() = 0;
};

/**

Lights are separate ungraph objects in scene! Cameras too!

One can add controller nodes to scenegraph to modify these asset states.

One can add lightController nodes to scenegraph to modify light state (properties, transform, etc.)
These are controller nodes with signal-slot type contact: ie.

controller c : public SignalSource
c.attach_property(float, "intensity")
light l
slot s = l.getSlot("intensity")
c.attach_slot_to_property("intensity", s)
Now:

source:
value -> slot.change()

slot; slot-ref
slot-ref:
sink-count
source-ptr: SignalSource


remove_sink(ptr)
    sink-count--
if sink-count = 0
    SignalSource->detach(this)

slot:
change(val)->sink = val

sink-parent:
slots: slot[] list

    sink
    source

When light is deleted:

*/


/** Scenegraph node. 
    Nodes contain: Properties and transforms.
*/

class SceneNode
{
public:
    typedef std::shared_ptr<SceneNode> shared_ptr;
    typedef SceneNode*                 ptr;
    typedef gr::integer_key             id_type;
    typedef std::list<Property::ptr>        property_collection;
    typedef std::list<shared_ptr>      node_collection;

private:
    std::string name_;
    id_type     id_;
    gr::Transform localTransform_;
    gr::Transform worldTransform_;

public:

    SceneNode(const id_type nodeid, const char* name = ""):id_(nodeid), name_(name){}
    virtual ~SceneNode() = 0;
    const std::string& name(){return name_;}
    id_type id(){return id_;}
};


class Node : public SceneNode
{
private:        
    SceneNode::property_collection properties_;
    SceneNode::node_collection     children_;

public:

    Node(const id_type nodeid, const char* name = ""):SceneNode(nodeid, name){}

    property_collection& properties(){return properties_;}
    node_collection&     children(){return children_;}

    static SceneNode::shared_ptr create(id_type id, const char* string)
    {
        SceneNode::shared_ptr ptr(new Node(id, string));
        return ptr;
    }
};

// Will engage renderer
class VisualNode : public SceneNode
{

};

// Walks the graph
class Walker
{
    /*
     * Evaluation order: 
     * 1) apply state variables
     * 2) render geometry
     */
    class TransformStack
    {

    };
   
    // transformstack transform
    // material activeMaterial
    // uniform list activeUniforms
    // geometry activegeometry

    Renderer* renderer_;
public:

    Walker(Renderer* renderer):renderer_(renderer)
    {}     

    void walk(Node* node)
    {
    }
};

/** Stores scene graph nodes */
class SceneGraph
{
    typedef std::map<Node::id_type, Node::shared_ptr> id_to_node_map;

    id_to_node_map          nodesById_; 
    gr::LinearKeyGenerator  keyGenerator_;

public:

    Node::shared_ptr newNode(const char* name)
    {
        auto id = keyGenerator_();
        auto node = Node::create(id, name);
        nodesById_[id] = node;
        return node;
    }

    Node::shared_ptr newNode()
    {
        using boost::lexical_cast;
        auto id = keyGenerator_();
        std::string name = "node_" + lexical_cast<std::string>(id);
        auto node = Node::create(id, name.c_str());
        nodesById_[id] = node;
        return node;
    }

};


}
}

